# integrated_trail_encryptor.py - 集成轨迹生成和加密功能
import requests
import json
import time
import random
import math
import cv2
import numpy as np
from typing import Dict, Any, Optional, List, Tuple
import os


def identify_gap(bg, tp, out):
    """
    bg: 背景图片
    tp: 缺口图片
    out:输出图片
    """
    # 读取背景图片和缺口图片
    bg_img = cv2.imread(bg)  # 背景图片
    tp_img = cv2.imread(tp)  # 缺口图片

    # 识别图片边缘
    bg_edge = cv2.Canny(bg_img, 100, 200)
    tp_edge = cv2.Canny(tp_img, 100, 200)

    # 转换图片格式
    bg_pic = cv2.cvtColor(bg_edge, cv2.COLOR_GRAY2RGB)
    tp_pic = cv2.cvtColor(tp_edge, cv2.COLOR_GRAY2RGB)

    # 缺口匹配
    res = cv2.matchTemplate(bg_pic, tp_pic, cv2.TM_CCOEFF_NORMED)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)  # 寻找最优匹配

    # 绘制方框
    th, tw = tp_pic.shape[:2]
    tl = max_loc  # 左上角点的坐标
    br = (tl[0] + tw, tl[1] + th)  # 右下角点的坐标
    cv2.rectangle(bg_img, tl, br, (0, 0, 255), 2)  # 绘制矩形
    cv2.imwrite(out, bg_img)  # 保存在本地

    # 返回缺口的X坐标
    return tl[0]


def find_gap_position_improved(
    background_path: str, slider_path: str, output_path: str = "result.jpg"
) -> Tuple[int, int]:
    """
    使用改进的边缘检测算法找到滑块需要移动到的位置

    Args:
        background_path: 带缺口的背景图路径
        slider_path: 缺口图片路径
        output_path: 输出标记图片的路径

    Returns:
        目标坐标 (x, y)
    """
    try:
        # 使用改进的识别算法
        gap_x = identify_gap(background_path, slider_path, output_path)

        # 读取滑块图片获取y坐标（通常滑块的y坐标相对固定）
        tp_img = cv2.imread(slider_path)
        if tp_img is not None:
            # 获取滑块图片的中心y坐标作为参考
            slider_height = tp_img.shape[0]
            gap_y = slider_height // 2  # 使用滑块高度的一半作为y坐标
        else:
            gap_y = 0  # 默认y坐标

        print(f"识别到缺口位置: x={gap_x}, y={gap_y}")
        print(f"识别结果已保存到: {output_path}")

        return (gap_x, gap_y)

    except Exception as e:
        print(f"缺口识别失败: {e}")
        # 返回默认位置
        return (100, 50)


def generate_realistic_trajectory(
    start_pos: Tuple[int, int], target_pos: Tuple[int, int]
) -> List[Tuple[int, int]]:
    """
    生成更真实的轨迹路径

    Args:
        start_pos: 起始位置 (x, y)
        target_pos: 目标位置 (x, y)

    Returns:
        轨迹坐标列表
    """
    start_x, start_y = start_pos
    target_x, target_y = target_pos

    coordinates = []

    # 总距离
    total_distance = abs(target_x - start_x)

    # 生成轨迹点
    current_x = start_x
    current_y = start_y

    # 添加起始点
    coordinates.append((current_x, current_y))

    # 模拟加速、匀速、减速的过程
    steps = random.randint(15, 25)  # 轨迹点数量

    for i in range(1, steps):
        progress = i / steps

        # 使用贝塞尔曲线模拟自然移动
        if progress < 0.3:  # 加速阶段
            speed_factor = progress * 3
        elif progress < 0.7:  # 匀速阶段
            speed_factor = 1.0
        else:  # 减速阶段
            speed_factor = 1.0 - (progress - 0.7) * 2

        # 计算新位置
        new_x = start_x + (target_x - start_x) * progress
        new_y = start_y + (target_y - start_y) * progress

        # 添加随机偏移模拟真实鼠标移动
        offset_x = random.randint(-2, 2) if i < steps - 3 else 0
        offset_y = random.randint(-1, 1) if i < steps - 3 else 0

        new_x = int(new_x + offset_x)
        new_y = int(new_y + offset_y)

        coordinates.append((new_x, new_y))

        current_x, current_y = new_x, new_y

    # 确保最后几个点接近目标
    coordinates.append((target_x, target_y))

    return coordinates


def generate_trajectory_object(
    tqdna: str, coordinates: List[Tuple[int, int]]
) -> Dict[str, Any]:
    """
    生成模拟的轨迹对象

    Args:
        tqdna: 唯一标识符字符串
        coordinates: 坐标点列表，格式为 [(x1, y1), (x2, y2), ...]

    Returns:
        包含完整轨迹数据的字典对象
    """
    # 生成基础时间戳（当前时间的毫秒）
    base_time = int(time.time() * 1000)
    start_time = base_time - random.randint(1000, 5000)
    current_time = start_time

    # 生成轨迹数据
    trail = []

    for i, (x, y) in enumerate(coordinates):
        if i == 0:
            # 第一个点
            trail.append(
                {
                    "x": x,
                    "y": y,
                    "t": current_time,
                    "s": round(random.uniform(0.5, 2.0), 4),
                    "tm": random.randint(80, 100),
                    "isd": 0,
                }
            )
            # 添加停止点
            current_time += random.randint(1, 3)
            trail.append({"x": x, "y": y, "t": current_time, "s": 0, "tm": 0, "isd": 0})
        else:
            # 计算与前一个点的距离和速度
            prev_x, prev_y = coordinates[i - 1]
            distance = math.sqrt((x - prev_x) ** 2 + (y - prev_y) ** 2)

            # 根据距离调整时间间隔
            time_interval = random.randint(6, 12)
            current_time += time_interval

            # 计算速度（像素/毫秒转换为合理的速度值）
            speed = round(distance / time_interval, 4)
            speed = max(0.1, min(speed, 20.0))  # 限制速度范围

            trail.append(
                {
                    "x": x,
                    "y": y,
                    "t": current_time,
                    "s": speed,
                    "tm": time_interval,
                    "isd": 0,
                }
            )

    # 在最后添加一些微调整点（模拟真实鼠标行为）
    last_x, last_y = coordinates[-1]
    for _ in range(random.randint(3, 8)):
        current_time += random.randint(5, 50)
        # 小幅度随机移动
        offset_x = random.randint(-3, 3)
        offset_y = random.randint(-2, 2)
        new_x = max(0, last_x + offset_x)
        new_y = max(0, last_y + offset_y)

        distance = math.sqrt(offset_x**2 + offset_y**2)
        speed = round(distance / random.randint(8, 16), 4) if distance > 0 else 0

        trail.append(
            {
                "x": new_x,
                "y": new_y,
                "t": current_time,
                "s": speed,
                "tm": random.randint(7, 20),
                "isd": 0,
            }
        )

        last_x, last_y = new_x, new_y

    # 生成点击事件
    click_time = current_time + random.randint(500, 1000)
    click = [{"x": last_x, "y": last_y, "t": click_time}]

    # 生成悬停事件
    hover_elements = [
        "div.verify-move-block",
        "div",
        "div#captcha-modal",
        "i.verify-icon.iconfont.icon-right",
    ]

    hover = []
    hover_time = start_time + random.randint(2000, 4000)

    for _ in range(random.randint(5, 10)):
        element = random.choice(hover_elements)
        duration = random.randint(4, 350)
        hover.append({"el": element, "dur": duration, "t": hover_time})
        hover_time += random.randint(100, 1000)

    # 生成结束时间
    end_time = click_time + random.randint(1000, 3000)

    # 生成浏览器信息
    browsers = [
        {"name": "Edge", "version": f"{random.randint(130, 140)}.0.0.0"},
        {"name": "Chrome", "version": f"{random.randint(120, 130)}.0.0.0"},
        {"name": "Firefox", "version": f"{random.randint(110, 120)}.0"},
    ]
    browser = random.choice(browsers)

    # 生成操作系统信息
    os_list = [
        {"name": "Windows", "version": "10"},
        {"name": "Windows", "version": "11"},
        {"name": "macOS", "version": "14.0"},
    ]
    os_info = random.choice(os_list)

    # 生成屏幕信息
    resolutions = [(1920, 1080), (1366, 768), (1440, 900), (1536, 864)]
    width, height = random.choice(resolutions)

    # 构建最终对象
    trajectory_object = {
        "trail": trail,
        "click": click,
        "hover": hover,
        "slider": [],
        "startTime": start_time,
        "endTime": end_time,
        "browser": browser,
        "os": os_info,
        "screen": {
            "resolution": {"width": width, "height": height},
            "colorDepth": 24,
            "pixelDepth": 24,
            "availableResolution": {
                "width": width,
                "height": height - 48,  # 减去任务栏高度
            },
        },
        "viewport": {
            "width": random.randint(80, 100),
            "height": random.randint(900, 950),
        },
        "tqdna": tqdna,
        "t": end_time,
    }

    return trajectory_object


def encrypt_trail_data(
    trail_data: Dict[str, Any], server_url: str = "http://localhost:3000"
) -> Optional[Dict[str, Any]]:
    """
    加密完整的轨迹数据

    Args:
        trail_data: 完整的轨迹数据字典
        server_url: 服务器地址

    Returns:
        加密结果字典或None（如果失败）
    """
    url = f"{server_url}/encrypt-trail"

    try:
        response = requests.post(url, json=trail_data, timeout=30)
        result = response.json()

        if result.get("success"):
            return {
                "original_size": result["originalDataSize"],
                "encrypted": result["encrypted"],
                "encrypted_size": len(result["encrypted"]),
                "compression_ratio": len(result["encrypted"])
                / result["originalDataSize"],
                "timestamp": result["timestamp"],
            }
        else:
            print(f"加密失败: {result.get('error', '未知错误')}")
            return None

    except requests.exceptions.RequestException as e:
        print(f"请求失败: {e}")
        return None
    except json.JSONDecodeError as e:
        print(f"响应解析失败: {e}")
        return None


def encrypt_coordinates(
    x: float, y: float, server_url: str = "http://localhost:3000"
) -> Optional[Dict[str, Any]]:
    """
    加密单个坐标（保持向后兼容）

    Args:
        x: x坐标
        y: y坐标
        server_url: 服务器地址

    Returns:
        加密结果字典或None（如果失败）
    """
    url = f"{server_url}/encrypt"
    data = {"x": x, "y": y}

    try:
        response = requests.post(url, json=data, timeout=10)
        result = response.json()

        if result.get("success"):
            return {
                "original": result["input"],
                "encrypted": result["encrypted"],
                "timestamp": int(time.time() * 1000),
            }
        else:
            print(f"坐标加密失败: {result.get('error', '未知错误')}")
            return None

    except Exception as e:
        print(f"坐标加密请求失败: {e}")
        return None


def generate_and_encrypt_trajectory(
    tqdna: str,
    coordinates: List[Tuple[int, int]],
    server_url: str = "http://localhost:3000",
) -> Optional[Dict[str, Any]]:
    """
    生成轨迹数据并加密的一体化函数

    Args:
        tqdna: 唯一标识符字符串
        coordinates: 坐标点列表，格式为 [(x1, y1), (x2, y2), ...]
        server_url: 加密服务器地址

    Returns:
        包含生成的轨迹数据和加密结果的字典，或None（如果失败）
    """
    # 生成轨迹数据
    trail_data = generate_trajectory_object(tqdna, coordinates)

    # 加密轨迹数据
    encrypted_result = encrypt_trail_data(trail_data, server_url)

    if encrypted_result:
        return {"trajectory": trail_data, "encryption": encrypted_result}
    else:
        return None


def quick_generate_and_encrypt(
    x: int, y: int, tqdna: str, server_url: str = "http://localhost:3000"
) -> Optional[str]:
    """
    快速生成和加密 - 基于单个终点坐标

    Args:
        x: 目标x坐标
        y: 目标y坐标
        tqdna: tqdna字符串
        server_url: 加密服务器地址

    Returns:
        加密后的字符串，或None（如果失败）
    """
    # 生成简单的轨迹路径（从随机起点到目标点）
    start_x = random.randint(50, 200)
    start_y = random.randint(50, 200)
    coordinates = [(start_x, start_y), (x, y)]

    result = generate_and_encrypt_trajectory(tqdna, coordinates, server_url)

    if result:
        return result["encryption"]["encrypted"]
    return None


def solve_captcha_with_images(
    background_path: str,
    slider_path: str,
    tqdna: str,
    server_url: str = "http://localhost:3000",
    start_pos: Tuple[int, int] = None,
    output_path: str = "gap_result.jpg",
) -> Dict[str, Any]:
    """
    使用改进的图片识别算法解决滑块验证码并生成轨迹

    Args:
        background_path: 带缺口的背景图路径
        slider_path: 滑块图片路径
        tqdna: tqdna字符串
        server_url: 加密服务器地址
        start_pos: 起始位置，如果不提供则使用默认值
        output_path: 识别结果图片保存路径

    Returns:
        包含识别结果和加密数据的字典
    """
    try:
        # 检查文件是否存在
        if not os.path.exists(background_path):
            raise FileNotFoundError(f"背景图文件不存在: {background_path}")
        if not os.path.exists(slider_path):
            raise FileNotFoundError(f"滑块图文件不存在: {slider_path}")

        print("开始识别缺口位置（使用改进算法）...")

        # 使用改进的识别算法
        target_pos = find_gap_position_improved(
            background_path, slider_path, output_path
        )

        # 设置起始位置
        if start_pos is None:
            start_pos = (random.randint(10, 30), target_pos[1] + random.randint(-5, 5))

        print(f"起始位置: {start_pos}")
        print(f"目标位置: {target_pos}")

        # 生成真实轨迹
        trajectory_coords = generate_realistic_trajectory(start_pos, target_pos)

        print(f"生成了 {len(trajectory_coords)} 个轨迹点")

        # 生成并加密轨迹
        result = generate_and_encrypt_trajectory(tqdna, trajectory_coords, server_url)

        if result:
            return {
                "success": True,
                "start_pos": start_pos,
                "target_pos": target_pos,
                "trajectory_points": len(trajectory_coords),
                "encrypted_data": result["encryption"]["encrypted"],
                "coordinate_result": {"x": target_pos[0], "y": target_pos[1]},
                "output_image": output_path,
                "full_result": result,
            }
        else:
            return {
                "success": False,
                "error": "轨迹加密失败",
                "start_pos": start_pos,
                "target_pos": target_pos,
                "output_image": output_path,
            }

    except Exception as e:
        return {"success": False, "error": str(e)}


def solve_captcha_simple(
    background_path: str,
    slider_path: str,
    tqdna: str,
    server_url: str = "http://localhost:3000",
) -> Optional[str]:
    """
    简化版滑块验证码求解，直接返回加密字符串

    Args:
        background_path: 带缺口的背景图路径
        slider_path: 滑块图片路径
        tqdna: tqdna字符串
        server_url: 加密服务器地址

    Returns:
        加密后的字符串或None
    """
    result = solve_captcha_with_images(background_path, slider_path, tqdna, server_url)

    if result.get("success"):
        return result["encrypted_data"]
    else:
        print(f"求解失败: {result.get('error')}")
        return None


if __name__ == "__main__":
    # 使用示例
    print("=== 改进版滑块验证码自动求解 ===")

    # 配置参数
    background_image = "background.jpg"  # 带缺口的背景图
    slider_image = "slider.jpg"  # 滑块图片
    tqdna = "102-3ba3e85cf7961244f473eb19390ded7fce52e443ae642873b84259744e1184472f259bdb689EAA555453202572a7e250"

    # 方法1: 完整版 - 返回详细信息
    result = solve_captcha_with_images(background_image, slider_image, tqdna)

    if result.get("success"):
        print("✓ 验证码求解成功!")
        print(f"  目标坐标: x={result['target_pos'][0]}, y={result['target_pos'][1]}")
        print(f"  轨迹点数: {result['trajectory_points']}")
        print(f"  加密结果: {result['encrypted_data'][:50]}...")
        print(f"  坐标对象: {result['coordinate_result']}")
        print(f"  识别结果图片: {result['output_image']}")
    else:
        print(f"✗ 验证码求解失败: {result.get('error')}")

    print("\n" + "=" * 50)

    # # 方法2: 简化版 - 直接返回加密字符串
    # encrypted = solve_captcha_simple(background_image, slider_image, tqdna)

    # if encrypted:
    #     print("✓ 简化版求解成功!")
    #     print(f"  加密结果: {encrypted[:50]}...")
    # else:
    #     print("✗ 简化版求解失败")
